
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>MySQL优化 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>MySQL优化 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">Java内存模型（JMM）的核心目标是什么？它试图解决什么问题？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">JMM的核心目标是定义一套在虚拟机中变量的访问规则，即变量如何存储到内存和从内存中取出。它通过屏蔽各种硬件和操作系统的内存访问差异，旨在让Java程序在各种平台下都能达到一致的内存访问效果，从而实现“一次编写，到处运行”的承诺。</div>
          </div>
          <div class="card-source">来源: 一、Java内存模型（JMM） -> 1.1 什么是JMM</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">JMM 定义的主内存和工作内存是什么关系？线程对变量的操作是如何通过它们进行的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">JMM规定所有变量都存储在主内存中，而每个线程拥有自己的工作内存，其中保存了被该线程使用的变量的主内存副本。线程对变量的所有操作（如读取、赋值）都必须在工作内存中进行，不能直接读写主内存。不同线程之间也无法直接访问对方工作内存中的变量，线程间变量值的传递均需要通过主内存来完成。</div>
          </div>
          <div class="card-source">来源: 一、Java内存模型（JMM） -> 1.2 主内存与工作内存</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">Java 并发编程中的三大特性是什么？JMM 是如何通过不同机制来保证这些特性的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">并发编程的三大特性是原子性、可见性和有序性。<br>1. 原子性：JMM保证了基本数据类型的读写操作是原子的。对于更大范围的原子性，可以使用 `synchronized` 关键字。<br>2. 可见性：JMM通过 `volatile`、`synchronized` 和 `final` 关键字保证可见性。`volatile`能保证新值立即同步到主内存并在使用前刷新。<br>3. 有序性：可以通过 `volatile` 和 `synchronized` 来保证。`volatile`本身禁止指令重排序，`synchronized`则保证同一时刻只有一个线程可以锁定和操作变量。</div>
          </div>
          <div class="card-source">来源: 一、Java内存模型（JMM） -> 1.3 JMM的三大特性</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">`volatile` 关键字是如何同时保证可见性和禁止指令重排序的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. 保证可见性：当一个线程修改 `volatile` 变量时，JMM会立即将该线程工作内存中的副本刷新到主内存。当其他线程读取该变量时，JMM会使其工作内存中的副本无效，强制从主内存重新读取。<br>2. 禁止指令重排序：JMM会为 `volatile` 变量的读写操作插入特定的内存屏障（Memory Barrier），以确保其前后的指令不会被重排序，保证代码执行的有序性。</div>
          </div>
          <div class="card-source">来源: 二、volatile关键字 -> 2.1 保证可见性 & 2.2 禁止指令重排序</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">为什么说 `volatile` 关键字不能保证像 `i++` 这样的复合操作的原子性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为 `i++` 操作并非原子操作，它至少包含三个步骤：读取 `i` 的值、将值加1、将新值写回 `i`。`volatile` 只能保证在读取步骤时能获取到最新的值，但无法保证在“读取-修改-写入”这整个过程中不被其他线程打断。如果两个线程同时执行 `i++`，它们可能读取到相同的旧值，导致最终结果小于预期。</div>
          </div>
          <div class="card-source">来源: 二、volatile关键字 -> 2.3 volatile无法保证原子性</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">在双重检查锁定（DCL）单例模式中，为何必须使用 `volatile` 来修饰单例实例变量？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为 `instance = new Singleton()` 这行代码不是原子操作，它会被编译为多个指令：分配内存、初始化对象、将 `instance` 引用指向内存地址。由于指令重排序的存在，步骤2和3的顺序可能颠倒。如果没有 `volatile`，一个线程可能在对象未完全初始化时就获取了其引用，从而导致使用了一个不完整的对象。`volatile` 通过禁止指令重排序，确保了对象必须在完全初始化后，其引用才能对其他线程可见。</div>
          </div>
          <div class="card-source">来源: 二、volatile关键字 -> 2.4.2 双重检查锁定（DCL）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">`final` 关键字除了表示不可变之外，在Java内存模型中还有什么特殊作用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">在JMM中，`final` 关键字能够确保对象的“安全发布”（Safe Publication）。JMM保证，在一个对象的构造函数内对 `final` 字段的写入，与之后将该对象的引用赋值给一个变量，这两个操作之间不能重排序。这意味着，只要对象被正确构造（无this引用逸出），任何线程在看到该对象的引用时，也必定能看到其所有 `final` 字段的正确初始化值。</div>
          </div>
          <div class="card-source">来源: 三、final关键字 -> 3.2 final与内存模型</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">什么是“this引用逸出”？它会对 `final` 字段的内存可见性保证产生什么破坏？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">“this引用逸出”是指在一个对象的构造函数还未执行完毕时，该对象的引用（this）就被发布给了其他线程。例如，在构造函数中启动一个新线程并传入 `this`。在这种情况下，其他线程可能会在 `final` 字段被初始化之前就访问到该对象，从而读取到 `final` 字段的默认值（如0或null），这破坏了 `final` 关键字提供的安全发布保证。</div>
          </div>
          <div class="card-source">来源: 三、final关键字 -> 3.2 final与内存模型</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">`final` 修饰的字段是何时被初始化的？它与普通字段有何不同？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`final` 字段的初始化时机有两种：<br>1. 在定义时直接赋值。<br>2. 在类的所有构造函数中都对其进行赋值。<br>与普通字段不同，`final` 字段一旦被赋值后就不能再改变。更重要的是，在并发环境中，JMM对 `final` 字段有特殊保证，确保其在对象引用对其他线程可见时，其值也必然是正确初始化后的值（前提是没有this引用逸出）。</div>
          </div>
          <div class="card-source">来源: 三、final关键字 -> 3.1 final的基本用法</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
